package cn.mvc.web.controller;

import cn.mvc.domain.User;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.UnsatisfiedServletRequestParameterException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;

/**
 * @author yexi
 */
@Controller
public class CustomController {

    /**
     * 定义一个处理请求的方法。
     * {@code @RequestMapping}: 用于将web请求映射到具有灵活方法签名的请求处理类中的方法的注解。
     * 属性解析:
     * value —— 表示的映射路径，别名path。比如说地址栏输入 {ip+端口号}/{value}，就会执行到下面的方法进行处理。
     * method —— 控制可以映射到的HTTP请求方式，从而可以缩小映射范围。默认是全部请求方式均命中。
     * params —— 控制请求参数。字符串匹配，不支持通配符。
     * 示例: param -- 表示请求中必须包含 param 参数。!param -- 表示请求中不能包含 param 参数。
     * param=value -- 表示请求中必须包含 param 参数，并且值必须是 value。
     * param!=value -- 表示请求中如果包含 param 参数，则值不能是 value。
     * headers —— 控制请求头。支持通配符。
     *
     * @see RequestMappingHandlerMapping#getMappingForMethod(Method, Class)
     * @see RequestMappingHandlerMapping#createRequestMappingInfo(RequestMapping, RequestCondition)
     * @see UnsatisfiedServletRequestParameterException#getMessage() 校验报错信息
     */
    @RequestMapping(
            value = {"/path", "/path1"},
            method = {RequestMethod.GET, RequestMethod.POST},
            params = {"username=root", "password", "!cardId", "email!=test@qq.com"},
            headers = "content-type=application/*"
    )
    public ModelAndView c1() {
        // 通过创建这个类对象，告诉 Spring MVC 找什么视图文件，往作用域或者说往模型中存入什么数据
        ModelAndView modelAndView = new ModelAndView();
        // 往作用域或者模型中存入数据，类似之前往 request 作用域存入数据
        modelAndView.addObject("msg", "方法返回类型是 ModelAndView");
        // 设置请求转发至的对应视图
        //modelAndView.setViewName("/WEB-INF/views/common.jsp");
        // 在配置视图解析器之后，就可以无需编写完整的请求转发地址，只需要提供页面的名称即可
        modelAndView.setViewName("common");
        return modelAndView;
    }

    /**
     * 如果返回值是 String，则表明指定了请求转发的路径。且同样的，会受到视图解析器的影响。
     * 这种情况下，要往模型中存入数据，则可以写一个 Model 类型的形参。
     */
    @RequestMapping("/path2")
    public String c2(Model model) {
        // 往作用域或者模型中存入数据
        model.addAttribute("msg", "方法返回类型是 String");
        return "common";
    }

    /**
     * ModelAndView 返回 null 表示返回一个空白页面
     */
    @RequestMapping("/path3")
    public ModelAndView c3() {
        return null;
    }

    /**
     * {@code @RequestParam}: 获取请求参数。示例: /req1?username==xxx&age=xxx 或者是置于请求体内也没有关系。
     * TIPS: 当形参包含多个可映射到的参数时，则均赋值。
     * TIPS: 当形参是数组时，则代表多个相同的传参，示例:sports=篮球&sports=足球，均会赋值给数组。
     * TIPS: 当形参是对象时，则赋值给对象里面的属性。
     * 属性解析:
     * value —— 表示对应的参数名，别名name。默认是""，表示使用方法形参名称充当。
     */
    @RequestMapping(path = "/req1", method = RequestMethod.GET)
    public String req1(@RequestParam("username") String name, Integer age, @RequestParam("age") Integer age2, String[] sports, User user) {
        System.out.println("接收到参数: " + name);
        System.out.println("接收到参数: " + age);
        System.out.println("接收到参数: " + age2);
        System.out.println("接收到参数: " + Arrays.toString(sports));
        System.out.println("接收到参数: " + user);
        return "common";
    }

    /**
     * 当形参是 Date 时，会出现类型转换异常，这时就需要指定@DateTimeFormat，如果是映射到对象里面的属性也要这么做。
     */
    @RequestMapping(path = "/req2", method = RequestMethod.POST)
    public String req2(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date, User user) {
        System.out.println("接收到参数: " + date);
        System.out.println("接收到参数: " + user);
        return "common";
    }

    /**
     * 请求转发
     */
    @RequestMapping("/resp1")
    public String forward() {
        return "forward:/WEB-INF/views/common.jsp";
    }

    /**
     * 重定向
     */
    @RequestMapping("/resp2")
    public String redirect() {
        return "redirect:/redirect.jsp";
    }

}